#!/usr/bin/env python
# $Id: dnssql.py 0cb950afef6b 2012/12/02 11:04:34 pgurumur $
# Copyright (c) 2009 Prabhu Gurumurthy  <pgurumur@gmail.com>
# 
# Permission to use, copy, modify, and distribute this software for any
# purpose with or without fee is hereby granted, provided that the above
# copyright notice and this permission notice appear in all copies.
#
# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
#

__version__  = "$Revision: 0cb950afef6b $"
__author__   = "$Author: pgurumur $"
__modified__ = "$Date: 2012-12-02 11:04:34Z $"

import ConfigParser
import datetime
import optparse
import re
import sys
import random
import socket
import warnings

from Cheetah.Template import Template
from Cheetah.NameMapper import NotFound
from sqlalchemy.exc import InvalidRequestError

from venya.lib.exception import GetOptError
from venya.lib import cmdparser
from venya.lib import ip
from venya.lib import sql
from venya.lib import log
from venya.lib import stdlib

class DnsConfig(object):
   def __init__(self, Template, DbHandle, DnsName):
      self._handle = None
      self._tmpl = None
      self._name = None
      self._fh = None
      self._dev = None
      self._devname = None
      self._flag = False
      self._log = log.StreamLog(stdlib.my_name(self))

      if DbHandle:
         self._handle = DbHandle
      else:
         self.error("no db handle provided!")
      
      if Template:
         self._tmpl = Template
      else:
         self.error("No template to generate DNS Zone file!")

      if DnsName:
         self._name = DnsName
      else:
         self.error("No DNS Name provided to generate DNS Zone config!")

      self._dnsconfig = "%s.zone" %self._name
      try:
         self._fh = open(self._dnsconfig, "w")
      except OSError as err:
         self.error(err.message)

      self._device = self._handle.device
      self._ipblock = self._handle.ipblock
      self._rr = self._handle.rr
      self._zone = self._handle.zone
      self._site = self._handle.site
      self._intf = self._handle.interface
      self._phyaddr = self._handle.physaddr
      self._product = self._handle.product
      self._entity = self._handle.entity


      self._dev = self._device.q.values(self._device.id, self._device.name,
            self._device.snmp_target, self._device.serialnumber,
            self._device.site, self._device.community, self._device.product)

   def close(self):
      if self._fh:
         self._fh.close()
         self._fh = None

   def warn(self, fmt, *args):
      mesg = stdlib.printf(fmt, *args)
      self._log(mesg)
      warnings.warn(mesg)

   def error(self, fmt, *args):
      self.close()
      mesg = stdlib.sprintf(fmt, *args)
      self._log(mesg)
      raise ValueError(mesg)

   def GetSite(self, SiteId):
      retval = None
      if SiteId and self._flag:
         [sitename for sitename in self._site.q.filter(
            self._site.id == SiteId).values(self._site.name)]
         if sitename:
            retval = sitename[0]

      return retval

   def VerifyZone(self, ZoneId):
      retval = None
      if ZoneId:
         [(zid, devname) for (zid, devname) in self._rr.q.filter(
            self._rr.id == ZoneId).values(self._rr.zone, self._rr.name)]
         [zname for zname in self._zone.q.filter(
            self._zone.id == zid).values(self._zone.name)]
         # if zname[0] == self._name:
         if zname[0] == "neteng.ssnsgs.net":
            self._flag = True
            retval = devname

      return retval

   def GetAddress(self, Address):
      retval = None
      if Address and self._flag:
         [(ipaddr, prefix, version) for (ipaddr,
            prefix, version) in self._ipblock.q.filter(
               self._ipblock.id == Address).values(self._ipblock.address,
                  self._ipblock.prefix, self._ipblock.version)]
         try:
            address = ip.ip(int(ipaddr), prefix = prefix, version = version)
         except ip.IPError as err:
            self.warn(err.message)
            self._flag = False
         else:
            retval = address

      return retval

   def GetInterfaceAddress(self, IfId):
      retval = None
      if IfId and self._flag:
         try:
            [(addr, prefix, version) for (addr,
               prefix, version) in self._ipblock.q.filter(
                  self._ipblock.interface == IfId).values(self._ipblock.address,
                           self._ipblock.prefix, self._ipblock.version)]
         except InvalidRequestError as err:
            self._flag = False
            self.warn(err.message)

         address = None
         try:
            address = ip.ip(int(addr), prefix = prefix, version = version)
         except UnboundLocalError as err:
            pass

         if address:
            retval = address

      return retval

   def GetPhyAddr(self, IfId):
      retval = None
      if IfId and self._flag:
         [address for address in self._phyaddr.q.filter(
            self._phyaddr.id == IfId).values(self._phyaddr.address)]
         if address:
            retval = str(address)

      return retval

   def GetInterfaces(self, DeviceId):
      retval = []
      if DeviceId and self._flag:
         reasaif = re.compile("Adaptive Security Appliance '(.*)'.*")
         refwif = re.compile("Firewall Services Module '(.*)'.*")
         for ix in self._intf.q.filter(self._intf.device == DeviceId).values(
               self._intf.id, self._intf.name, self._intf.speed,
               self._intf.type, self._intf.physaddr):
            ifip = self.GetInterfaceAddress(int(ix[0]))
            name = None
            if ix[1].find("Adaptive") >= 0:
               match = reasaif.search(ix[1])
               if match:
                  name = match.groups()[0]
            elif ix[1].find("Firewall Services Module") >= 0:
               match = refwif.search(ix[1])
               if match:
                  name = match.groups()[0]
            else:
               name = ix[1]

            iftype = None
            if ix[3] == "propVirtual":
               iftype = "virtual"
            elif ix[3] == "l2vlan":
               iftype = "vlan"
            elif ix[3] == "softwareLoopback":
               iftype = "loopback"
            elif ix[3] == "ethernetCsmacd":
               iftype = "ethernet"
            elif ix[3] == "tunnel":
               iftype = ix[3]
            elif ix[3] == "other":
               iftype = "unknown"

            phyaddr = self.GetPhyAddr(int(ix[4]))
            temp = {}
            if phyaddr and ifip:
               temp[name] = (ifip, phyaddr)
            elif ifip:
               temp[name] = (ifip, )

            if temp:
               retval.append(temp)

      return retval

   def printconfig(self):
      time = datetime.date.today()
      config = {}
      config["dnsName"] = self._name
      config["serverName"] = socket.gethostname()
      config["serial"] = time.strftime("%Y%m%d") + str(random.randint(10, 99))
      template = Template(file = self._tmpl, searchList = [config])
      try:
         if self._fh:
            self._fh.write(str(template))
      except NotFound as err:
         self.error(err.message)

      for ix in self._dev:
         self._devname = self.VerifyZone(int(ix[1]))
         device = self._devname
         site = self.GetSite(int(ix[4]))
         self._devip = self.GetAddress(int(ix[2]))
         self._devifs = self.GetInterfaces(int(ix[0]))

         model = None
         if device:
            product = self._handle.product
            entity = self._handle.entity;
            [(name, opsys, entid) for (name, opsys, entid) in product.q.filter(
               product.id == ix[6]).values(
                  product.name, product.latest_os, product.manufacturer)]
            [entname for entname in entity.q.filter(
               entity.id == int(entid)).values(entity.name)]
            model = "%s.%s" %(entname[0], name)

         if device and self._devip and self._flag:
            self._fh.write("%s\t\t180\t\tIN\tA\t\t%s\n" %(
               device, str(self._devip)))

         record = None
         if device and model and self._flag:
            record = "%s | %s | %s | %s" %(device, ix[3], model, ix[5])

         if record:
            self._fh.write("%s\t\t180\t\tIN\tTXT\t\"%s\"\n" %(device, record))

         if device and self._devifs and self._flag:
            for item in self._devifs:
               name = item.keys()[0]
               addr = phy = None
               try:
                  (addr, phy) = item.values()[0]
               except ValueError as err:
                  (addr, ) = item.values()[0]
   
               if addr and self._flag:
                  temp = name.replace("/", "-")
                  temp = temp.replace(":", "_")
                  if len(addr) == 32:
                     self._fh.write("%s.%s\t\t180\t\tIN\tA\t\t%s\n" %(
                        temp.lower(), device, addr))
                  else:
                     if addr.version == 4:
                        self._fh.write("%s.%s\t\t180\t\tIN\tAPL\t\t1:%s\n" %(
                           temp.lower(), device, addr))
                     elif addr.version == 6:
                        self._fh.write("%s.%s\t\t180\t\tIN\tAPL\t\t2:%s\n" %(
                           temp.lower(), device, addr))

         self._flag = False


class dnssql(cmdparser.GetOpt):
   option_list = (
         optparse.make_option("-t", "--template", default = "dns.tmpl",
            help = "Template File to use for DNS", type = "str", dest = "tmpl"),
         optparse.make_option("-c", "--config", default = "dnssql.cfg",
            help = "Config File for MySQL database", type = "str",
            dest = "config"),
         optparse.make_option("-n", "--name", type = "str",
            help = "DNS name to be used on zone file", dest = "dnsname"),
         ) + cmdparser.GetOpt.option_list

   def __init__(self, **Keywords):
      super(self.__class__, self).__init__(**Keywords)
      self._netdot = None

   def handle(self):
      (options, args) = self.parse()

      if options.config:
         parser = ConfigParser.SafeConfigParser()
         parser.read(options.config)

         try:
            self._netdot = sql.mysql(parser.get("Netdot", "dbname"),
                  parser.get("Netdot", "dbhost"),
                  parser.get("Netdot", "dbuser"),
                  parser.get("Netdot", "dbpass"))
         except sql.SQLError as err:
            self.error(err.message)

         handle = None
         try:
            handle = DnsConfig(options.tmpl, self._netdot, options.dnsname)
         except ValueError as err:
            self.error(err.message)
         else:
            handle.printconfig()
         finally:
            if handle:
               handle.close()

if __name__ == "__main__":
   dnshandle = None
   try:
      dnshandle = dnssql(version = 1.0, args = sys.argv)
      if len(dnshandle) <= 1:
         dnshandle.printhelp()
      else:
         dnshandle.handle()
   except GetOptError, message:
      print(message)
      sys.exit(1)
